import { AnimatorOptions, AnimatorResult, UIContext } from "@kit.ArkUI";

/**
 * # 完全可控的分步帧动画
 *
 * 分步动画，完全可控。分几步、每步的长度、步与步之间的暂停时间、每次循环之间的暂停时间、动画插值曲线（仅支持部分系统曲线）等等完全由你定义。
 *
 * # 核心思想
 *
 * - _steps → 控制每段动画长度
 * - _stepDelays → 控制暂停时长、停顿节奏
 * - _direction + iterations → 控制循环与往返
 * - _onFrame → 更新 UI/状态
 * - _onFinish → 动画完成后的逻辑
 *
 * # 可以做的事情
 * 1. 逐步展示/加载动画
 * - 比如一个进度条或装填动画，每一段可以有不同的长度和停顿时间。
 * - 示例：进度条前 30% 慢慢加载，中间 50% 快速加载，最后 20% 再慢慢完成。
 * - 用 _steps + _stepDelays 可以轻松实现“曲线式”进度动画。
 *
 * 2. 游戏元素的分段动作
 * - 比如角色攻击动作：角色抬手 → 挥剑 → 回收
 * - 每一小段用不同步长控制，停顿可让动作更有节奏感。
 * - _onFrame 里可以更新角色位置/旋转/缩放。
 *
 * 3. UI 元素的节奏闪烁/跳动
 * - 做呼吸灯、心跳效果、按钮闪烁。
 * - 通过 _steps 不同长度 + _stepDelays 控制节奏，甚至可以模拟自然节奏（不均匀的心跳）。
 * - 结合 alternate 方向，动画会自动往返循环。
 *
 * 4. 复杂图表或条形图动画
 * - 每根柱子分多步慢慢涨高。
 * - 可以让每步长度不同，模拟“数据增长曲线”。
 * - _onFrame 可以在每步回调里更新图表。
 *
 * 5. 像素/LED 灯阵列效果
 * - 如果你做 LED 灯带、像素动画：
 * - 每一步对应一部分像素亮起
 * - _stepDelays 控制闪烁节奏
 * - 可以做跑马灯、流水灯、呼吸灯等创意效果。
 *
 * 6. 文字或 UI 元素逐字/逐块出现
 * - 用 _steps 定义每段文字或 UI 元素出现的“长度”或进度
 * - _stepDelays 定义每块停顿时间
 * - 可以做“打字机效果”，每块文字有停顿，甚至停顿不均匀，更自然。
 *
 * 7. 音乐可视化
 * - 动画跟随音乐节奏变化
 * - _steps 对应节奏强弱，_stepDelays 控制停顿
 * - 可做音符跳动、节奏波形动画
 */
export class StepAnimation {

  private uiContext: UIContext

  private result?: AnimatorResult

  private count: number = 0 //循环次数

  private _onFrame: (progress: number) => void = () => {};

  /**
   * 帧动画过程回调
   * @param value
   * @returns
   */
  onFrame(value: (progress: number) => void) {
    this._onFrame = value;
    return this
  }

  private _onFinish: () => void = () => {};

  /**
   * 动画结束回调
   * @param value
   * @returns
   */
  onFinish(value: () => void) {
    this._onFinish = value
    return this
  }

  private _onCancel: () => void = () => {}

  onCancel(value: () => void) {
    this._onCancel = value
    return this
  }

  /**
   * 动画插值起点。默认值: 0
   */
  private _begin: number = 0;

  /**
   * 动画插值终点。默认值: 1
   */
  private _end: number = 1;

  /**
   * 一个完整动画共有多少步
   */
  private _step: number = 1;

  /**
   * 自定义间隔，数组长度比_stepDelays的长度大1。
   * 数组长度与_step是等价的。
   * 如果同时设置了_step和_steps，则使用_steps
   */
  private _steps: number[] = []

  /**
   * 当前是第几步。
   */
  private _currentStep: number = 1;

  /**
   * 动画延时播放时长，单位毫秒
   */
  private _delay: number = 0;

  /**
   * 动画播放的时长，单位毫秒。
   */
  private _duration: number = 1000;

  /**
   * 动画正向播放结束时的暂停时长，单位毫秒。默认值：0
   */
  private _startDelay: number = 0;

  /**
   * 动画反向播放结束时的暂停时长，单位毫秒。默认值：0
   */
  private _endDelay: number = 0;

  /**
   * 均匀间隔，每两步之间的暂停时长相同，单位毫秒。默认值：0
   *
   * 如果同时设置了_stepDelay 和 _stepDelays，优先使用_stepDelays
   */
  private _stepDelay: number = 0;

  /**
   * 每两步之间的暂停时长数组，单位毫秒。数组长度为 _step - 1。
   *
   * 例如：_step = 4, 则_stepDelays应该是[200, 300, 400]，
   *
   * 即第一步与第二步之间暂停200毫秒，第二步与第三步之间暂停300毫秒，第三步与第四步之间暂停400毫秒。
   *
   * 如果同时设置了_stepDelay 和 _stepDelays，优先使用_stepDelays
   */
  private _stepDelays: number[] = []

  private _iterations: number = -1;
  private _fill: "none" | "forwards" | "backwards" | "both" = 'both';
  private _direction: "normal" | "reverse" | "alternate" | "alternate-reverse" = 'alternate';
  private _easing: string = 'linear';

  // 动画插值起点与终点之间的距离
  private totalLen: number = 0
  private stepSize: number = 0
  private _tempLen: number = 0  //正向动画时，_steps走过的步数总长度
  private _rTempLen: number = 0 //反向动画时，_steps走过的步数总长度（倒数）

  constructor(context: UIContext) {
    this.uiContext = context
  }

  private getOptions(): AnimatorOptions {
    return {
      duration: this._duration,
      delay: this._delay,
      iterations: this._iterations,
      fill: this._fill,
      direction: this._direction,
      begin: this._begin,
      end: this._end,
      easing: this._easing
    }
  }

  private initData() {
    this.totalLen = 0
    let len = Math.abs(this._end - this._begin)
    let stepInterval = len / this._step

    if (!this._steps.length) {//如果没有设置_steps，则使用_end、_begin与_step
      for (let i = 0; i < this._step; i++) {
        this._steps.push(stepInterval)
      }
      this.totalLen = len
    } else {//如果设置了_steps，则直接忽略_end、_begin、_step
      this._steps.forEach((s) => {
        this.totalLen += s
      })
      this._end = this.totalLen
    }

    this.stepSize = this._steps.length

    const isReverse = this._direction === 'reverse' || this._direction === 'alternate-reverse'

    this._currentStep = isReverse ? this.stepSize : 1

    //如果没有设置_stepDelays 或者 _stepDelays的长度不是 _steps.length - 1, 则使用_stepDelay填充
    //注意：_stepDelays数组长度比_steps数组长度小1，所以是stepSize - 1

    if (this._stepDelays.length && this._stepDelays.length != this.stepSize - 1) {
      console.log('_stepDelays的长度不等于 _steps的长度 - 1！需要使用_stepDelay替换。')
      this._stepDelays.splice(0)
    }

    if (!this._stepDelays.length) {
      for (let i = 0; i < this.stepSize - 1; i++) {
        this._stepDelays.push(this._stepDelay)
      }
    }
  }

  build() {
    this.initData()

    let options = this.getOptions()
    console.log(JSON.stringify(options))

    this.result = this.uiContext.createAnimator(options)

    this.result.onFrame = (pro) => {
      this._onFrame(pro)

      let _pro = this._begin > this._end ? pro - this._end : pro - this._begin

      //正向动画
      if (this._direction === 'normal' ||
        (this._direction === 'alternate' && this.count % 2 === 0) ||
        (this._direction === 'alternate-reverse' && this.count % 2 === 1)) {

        this._tempLen = 0

        for (let i = 0; i < this.stepSize - 1; i++) {
          this._tempLen += this._steps[i]

          if (_pro >= this._tempLen && this._currentStep == i + 1) {
            this._currentStep += 1
            let _d = this._stepDelays[i]
            this.pauseStep(_d > 0 ? _d : this._stepDelay)
          }
        }
      }

      //反向动画
      if (this._direction === 'reverse' ||
        (this._direction === 'alternate' && this.count % 2 === 1) ||
        (this._direction === 'alternate-reverse' && this.count % 2 === 0)) {

        this._rTempLen = 0

        for (let i = this.stepSize - 1; i > 0; i--) {
          this._rTempLen += this._steps[i];

          if (_pro <= this.totalLen - this._rTempLen && this._currentStep === i + 1) {
            this._currentStep -= 1
            let _d = this._stepDelays[i - 1]
            this.pauseStep(_d > 0 ? _d : this._stepDelay)
          }
        }
      }
    }

    this.result.onFinish = () => {
      this._tempLen = 0
      this._rTempLen = 0

      this._onFinish()
    }

    this.result.onCancel = () => {
      this._tempLen = 0
      this._rTempLen = 0

      this._onCancel()
    }

    this.result.onRepeat = () => {

      this.count ++

      if (this._direction == 'normal') {
        this._currentStep = 1
      } else if (this._direction == 'reverse') {
        this._currentStep = this.stepSize
      }

      if (this.count % 2 == 1) {
        if (this._endDelay > 0) {
          this.pauseStep(this._endDelay)
        }
      } else {
        if (this._startDelay > 0) {
          this.pauseStep(this._startDelay)
        }
      }
    }
  }

  /**
   *
   * 重置动画参数
   * @param isPlayNow 是否立即播放， 默认：否
   */
  reset(isPlayNow: boolean = false) {
    if (!this.result) {
      return this
    }

    this.finish()
    this.initData()

    this.result?.reset(this.getOptions())

    if (isPlayNow) {
      this.play()
    }

    return this
  }

  private isPausing: boolean = false
  private timeoutId: number = -1

  /**
   * 步与步之间的暂停
   * @param delay
   */
  private pauseStep(delay: number = this._stepDelay) {

    if (delay <= 0) {
      return
    }

    if (this.isPausing) {
      return
    }
    this.isPausing = true

    this.result?.pause()
    this.timeoutId = setTimeout(() => {
      this.result?.play()
      this.isPausing = false
    }, delay)
  }

  play() {
    if (!this.checkBuild()) {
      return
    }

    clearTimeout(this.timeoutId)
    this.isPausing = false

    this.result?.play()
  }

  pause() {
    if (!this.checkBuild()) {
      return
    }
    this.result?.pause()
  }

  finish() {
    if (!this.checkBuild()) {
      return
    }
    this.result?.finish()
  }

  cancel() {
    if (!this.checkBuild()) {
      return
    }
    this.result?.cancel()
  }

  private checkBuild(): boolean {
    if (!this.result) {
      console.log('StepAnimation 还没有构建，请先执行build()方法。')
      return false
    }

    return true
  }

  /**
   * 动画插值起点。默认值: 0
   */
  begin(value: number) {
    if (value < 0) {
      value = 0
    }

    this._begin = value;
    return this
  }

  /**
   * 动画插值终点。默认值: 1
   */
  end(value: number) {
    this._end = value;
    return this
  }

  /**
   * 一个完整动画共有多少步, 默认值：1， 范围[0, 99]
   */
  step(value: number) {
    if (value <= 0) {
      value = 1
    }

    if (value > 99) {
      value = 99
    }

    this._step = value;
    return this
  }

  /**
   * 使用数组自定义每步的长度
   *
   * ## 注意：
   *
   * - 如果同时设置_steps 和 _stepDelays， 后者的长度要比前者小1
   * - 设置了_steps后，_end的值会被设置成_steps的长度，即方法end()会失效
   *
   * @param value
   */
  steps(value: number[]) {

    this._steps = value

    return this
  }

  /**
   * 动画延时播放时长，单位毫秒。系统自行检查约束
   */
  delay(value: number) {
    this._delay = value;
    return this
  }

  /**
   * 动画播放的时长，单位毫秒。系统自行检查约束
   */
  duration(value: number) {
    this._duration = value;
    return this
  }

  /**
   * 动画正向播放结束时的暂停时长，单位毫秒。默认值：0
   */
  startDelay(value: number) {
    if (value < 0) {
      value = 0
    }

    this._startDelay = value;

    return this
  }

  /**
   * 动画反向播放结束时的暂停时长，单位毫秒。默认值：0
   */
  endDelay(value: number) {
    if (value < 0) {
      value = 0
    }

    this._endDelay = value;
    return this
  }

  /**
   * 均匀间隔，每两步之间的暂停时长相同，单位毫秒。默认值：0
   *
   * 如果同时设置了_stepDelay 和 _stepDelays，优先使用_stepDelays
   */
  stepDelay(value: number) {
    if (value < 0) {
      value = 0
    }
    this._stepDelay = value;
    return this
  }

  /**
   * 每两步之间的暂停时长数组，单位毫秒。数组长度为 _step - 1。
   *
   * 例如：
   *
   * _step = 4, 则_stepDelays应该是[200, 300, 400]，
   * 即第一步与第二步之间暂停200毫秒，第二步与第三步之间暂停300毫秒，第三步与第四步之间暂停400毫秒。
   *
   * 注意：
   * -
   * - 如果同时设置了_stepDelay 和 _stepDelays，优先使用_stepDelays；
   *
   * - 如果数组不为空，但是有不合法的值，这时候也会使用_stepDelay。
   *
   * - 如果同时设置_steps 和 _stepDelays， 后者的长度要比前者小1
   */
  stepDelays(value: number[]) {
    this._stepDelays = value
    return this
  }

  iterations(value: number) {
    this._iterations = value;
    return this
  }

  fill(value: "none" | "forwards" | "backwards" | "both") {
    this._fill = value;
    return this
  }

  direction(value: "normal" | "reverse" | "alternate" | "alternate-reverse") {
    this._direction = value;
    return this
  }

  easing(value: string) {
    this._easing = value;
    return this
  }
}